/*
 * FolderManager.cpp
 *
 *  Created on: 04.05.2011
 *      Author: niea
 */

#include "FolderManager.hpp"
#include "../common/log.hpp"
#include "../common/Utils.hpp"
#include "../common/MyIP.hpp"
#include <cstdio>
#include <cstdlib>
#include <dirent.h>
#include <cstring>
#include <iostream>
#include <sys/stat.h>
#include <sys/types.h>


#include "../tcp/TCPMessenger.h"
#include "../file-client/FileClientI.hpp"

using namespace efs;
using namespace rapidxml;

efs::FolderManager::FolderManager(std::string const& root_path, int folder_monitor_period, Settings const& settings)
	: FolderManagerI(root_path, folder_monitor_period, settings)
	, m_stop_req(false)
	, m_stop_monitoring(false)
	, m_activate_transmission(false)
	, m_receive_enable(true)
{
	if (m_root_path[m_root_path.size() - 1] != '/')
	{
		m_root_path  += '/';
	}
	m_folder_description.setRootPath(m_root_path);
	pantheios::log_INFORMATIONAL("[FolderManager]: root_path=" +  m_root_path);
}

efs::FolderManager::~FolderManager()
{
}

std::string efs::FolderManager::getXMLString(xml_document<> const &xml)
{
	std::string xml_string;
	rapidxml::print(std::back_inserter(xml_string), xml);
	return xml_string;
}

void efs::FolderManager::perseXML(xml_document<> &xml, std::string const& xml_string)
{
	std::vector<char> xml_vector(xml_string.begin(), xml_string.end());
	xml_vector.push_back('\0');
	xml.clear();
	xml.parse<rapidxml::parse_declaration_node | rapidxml::parse_no_data_nodes>(&xml_vector[0]);
}


void efs::FolderManager::processTCPMessage(std::string const& message)
{
	pantheios::log_INFORMATIONAL("[FolderManager]: TCP msg get");
	if (m_receive_enable)
	{
		compareWithRemoteFolder(message);
		m_receive_enable = false;
	}
}


void efs::FolderManager::compareWithRemoteFolder(std::string remote_folder_xml_string)
{

	//std::cout << remote_folder_xml_string << std::endl;

	m_stop_monitoring = true;

	std::vector<char> xml_vector(remote_folder_xml_string.begin(), remote_folder_xml_string.end());
	xml_vector.push_back('\0');
	xml_document<> remote_xml;
	remote_xml.parse<rapidxml::parse_declaration_node | rapidxml::parse_no_data_nodes>(&xml_vector[0]);
	std::string remote_root_path = remote_xml.first_node("root")->first_node("folder")->first_attribute("path")->value();

	FolderDescription remote_folder_description;
	if (remote_xml.first_node("root")->first_attribute("first-announce"))
	{
		m_activate_transmission = true;
	}
	else
	{
		m_activate_transmission = false;
	}

	remote_folder_description.fromXML(remote_xml, remote_xml.first_node("root"));

	std::vector<FileDescription> get_list, remove_list;
	m_folder_description.compareExternal(remote_folder_description.getFolderDescription(), get_list, remove_list, m_activate_transmission);

	std::vector<std::string> get_files, new_folders, remove_files, remove_folders;
	m_folder_description.splitEntryList(get_list, get_files, new_folders);
	m_folder_description.splitEntryList(remove_list, remove_files, remove_folders);

	pantheios::log_DEBUG("get list:");
	for (int i = 0; i != get_list.size(); ++i)
	{
		get_list[i].print();
	}

	pantheios::log_DEBUG("remove list:");
	for (int i = 0; i != remove_list.size(); ++i)
	{
		remove_list[i].print();
	}

	removeFiles(remove_files);
	removeFolders(remove_folders);

	makeNewFolders(new_folders);

	ClientEntry entry;
	entry.putXMLString(remote_folder_xml_string);
	getFiles(entry, remote_root_path, get_files);

	m_folder_description.applyMyChanges(get_list);

	m_stop_monitoring = false;
}


void efs::FolderManager::removeFiles(std::vector<std::string> const& list)
{
	std::vector<std::string>::const_iterator it = list.begin();
	for(; it != list.end(); ++it)
	{
		remove((m_root_path + *it).c_str());
	}
}

void efs::FolderManager::removeFolders(std::vector<std::string> const& list)
{
	std::vector<std::string>::const_iterator it = list.begin();
	for(; it != list.end(); ++it)
	{
		system(("rm -rf \"" + m_root_path + *it + "\"").c_str());
	}
}

void efs::FolderManager::makeNewFolders(std::vector<std::string> const& list)
{
	std::vector<std::string>::const_iterator it = list.begin();
	for(; it != list.end(); ++it)
	{
		//TODO: is 0766 right?
		mkdir((m_root_path + *it).c_str(), 0766);
	}
}


void efs::FolderManager::getFiles(ClientEntry &remote_entry, std::string const& remote_root_path, std::vector<std::string> const &list)
{
	std::vector<std::string>::const_iterator it = list.begin();
	for(; it != list.end(); ++it)
	{
		pantheios::log_DEBUG("[FolderManager]: Getting File: " + *it);
		if (!m_file_client->getFile(remote_entry, remote_root_path, *it, m_root_path))
		{
			remove((m_root_path + *it).c_str());
		}
	}
}


bool efs::FolderManager::start()
{
	if (pthread_create (&m_thread, NULL, FolderManager::threadProcStarter, (void*)this))
	{
		pantheios::log_ERROR("[FolderManager]: error creating thread");
		return false;
	}
	pantheios::log_INFORMATIONAL("[FolderManager]: thread start");
	//m_folder_description.scanFolder();
	return true;
}

void efs::FolderManager::stop()
{
	pantheios::log_INFORMATIONAL("[FolderManager]: thread stop request");
	m_stop_req = true;
	pthread_join(m_thread, NULL);
}

void * efs::FolderManager::threadProcStarter(void *base_class_pointer)
{
	static_cast<FolderManager*> (base_class_pointer)->threadProc();
	return 0;
}

rapidxml::xml_node<> *efs::FolderManager::prepareXML(rapidxml::xml_document<> &xml) const
{
	xml.clear();
	xml_node<>* decl = xml.allocate_node(node_declaration);
	decl->append_attribute(xml.allocate_attribute("version", "1.0"));
	decl->append_attribute(xml.allocate_attribute("encoding", "utf-8"));
	xml.append_node(decl);
	xml_node<> *root = xml.allocate_node(node_element, "root");
	xml.append_node(root);
	return root;
}

void efs::FolderManager::threadProc()
{
	sleep(1);
	while(1)
	{
		for (int i = 0; i < m_folder_monitor_period; ++i)
		{
			sleep(1);
			if (m_stop_req)
			{
				pantheios::log_DEBUG("[FolderManager]: Exit on user command");
				return;
			}
		}
		if (m_stop_monitoring)
		{
			continue;
		}

		xml_document<> xml;

		if (m_folder_description.scanFolder() || m_activate_transmission)
		{
			pantheios::log_DEBUG("[FolderManager]: Folder changed");
			pantheios::log_DEBUG("[FolderManager]: Checking stability");

			do
			{
				sleep(1);
				pantheios::log_DEBUG("[FolderManager]: Folder is not stable");
			}while (m_folder_description.scanFolder() && !m_stop_req && !m_activate_transmission);

			pantheios::log_DEBUG("[FolderManager]: Folder is stable");

			xml_node<> *root = prepareXML(m_xml);
			m_folder_description.toXML(m_xml, root);

			m_settings.my_net_settings.appendToExternalXml(m_xml, root);

			m_stop_monitoring = true;
			m_activate_transmission = false;
			m_tcp_messenger->sendMessages(getXMLString(m_xml));
			m_stop_monitoring = false;
		}
		m_receive_enable = true;
	}

}


